package com.morning.dp.tools.dp;

import com.morning.dp.tools.dp.context.AbstractContext;
import com.morning.dp.tools.dp.context.ParameterizeContext;
import com.morning.dp.tools.dp.context.ParameterizeContextAdaptor;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * （装饰者/过滤器链模式）通用包装处理器链
 *
 * @author @阿秋
 * @date 2021/2/3
 */
public class DPWrapperChain<T> extends BaseDesignPattern<DPWrapperChain<T>> {


    private final ChainContext<T> WRAPPER_CHAIN_CONTEXT;


    private DPWrapperChain(ChainContext<T> chainContext) {
        super(chainContext);
        this.WRAPPER_CHAIN_CONTEXT = chainContext;
    }

    public static <T> DPWrapperChain<T> createWrapperChain(Class<T> returnType) {
        return new DPWrapperChain<>(new ChainContext<>(new HashMap<>()));
    }

    public DPWrapperChain<T> addWrapper(Function<ChainContext<T>, T> handlerWrapper) {
        WRAPPER_CHAIN_CONTEXT.HANDLER_WRAPPER_LIST.addFirst(handlerWrapper);
        return this;
    }

    public T invoke(Runnable originInvoker) {
        return invoke(paramContext -> {
            originInvoker.run();
            return null;
        });
    }

    public T invoke(Supplier<T> originInvoker) {
        return invoke(paramContext -> {
            return originInvoker.get();
        });
    }

    public T invoke(Consumer<ParameterizeContext> originInvoker) {
        return invoke(paramContext -> {
            originInvoker.accept(paramContext);
            return null;
        });
    }

    public T invoke(Function<ParameterizeContext, T> originInvoker) {
        this.WRAPPER_CHAIN_CONTEXT.originInvoker = originInvoker;
        return doInvokeChain(WRAPPER_CHAIN_CONTEXT);
    }

    private static <T> T doInvokeChain(ChainContext<T> chainContext) {
        if (chainContext.HANDLER_WRAPPER_LIST == null || chainContext.HANDLER_WRAPPER_LIST.isEmpty()) {
            chainContext.result = chainContext.originInvoker.apply(new ParameterizeContextAdaptor(chainContext));
            return chainContext.result;
        }
        Iterator<Function<ChainContext<T>, T>> iterator = chainContext.HANDLER_WRAPPER_LIST.iterator();
        if (iterator.hasNext() && !chainContext.isInterrupt()) {
            Function<ChainContext<T>, T> handlerWrapper = iterator.next();
            iterator.remove();
            chainContext.result = handlerWrapper.apply(chainContext);
        }
        return chainContext.result;
    }

    public static class ChainContext<T> extends AbstractContext {

        private final LinkedList<Function<ChainContext<T>, T>> HANDLER_WRAPPER_LIST;

        private T result;

        private Function<ParameterizeContext, T> originInvoker;

        public ChainContext(Map<String, Object> params) {
            super(params);
            this.HANDLER_WRAPPER_LIST = new LinkedList<>();
        }

        public T execute() {
            return doInvokeChain(this);
        }

    }
}
